package com.googlecode.perfectable.persistable.test;

import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

import javax.annotation.Nullable;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.google.common.collect.Iterables;
import com.googlecode.perfectable.persistable.DeletingFileVisitor;
import com.googlecode.perfectable.persistable.index.Indexer;
import com.googlecode.perfectable.persistable.index.Reference;
import com.googlecode.perfectable.persistable.serialization.ClassBasedSeparator;
import com.googlecode.perfectable.persistable.serialization.Separator;
import com.googlecode.perfectable.persistable.session.InvalidReference;
import com.googlecode.perfectable.persistable.storage.Storage;
import com.googlecode.perfectable.persistable.storage.file.FileStorage;
import com.googlecode.perfectable.persistable.storage.memory.MemoryStorage;

public class Functional {

	private static class Entity implements Serializable {
		private static final long serialVersionUID = 1L;

		public static final Function<Entity, String> VALUE_EXTRACTOR = (input) -> input.value;
		@SuppressWarnings("unused")
		public static final Function<Entity, Entity> PARENT_EXTRACTOR = (input) -> input.parent.orElse(null);

		private final String value;
		private final Optional<Entity> parent;

		public Entity(String value) {
			this.value = value;
			this.parent = Optional.empty();
		}

		public Entity(String value, Entity parent) {
			this.value = value;
			this.parent = Optional.of(parent);
		}

		@Override
		public boolean equals(@Nullable Object obj) {
			if(obj == this) {
				return true;
			}
			if(!(obj instanceof Entity)) {
				return false;
			}
			Entity other = (Entity) obj;
			return Objects.equals(this.value, other.value);
		}

		@Override
		public int hashCode() {
			return Objects.hashCode(this.value);
		}

	}

	private final Separator separator = new ClassBasedSeparator(Entity.class);

	private Path directory;

	@Before
	public void setupDirectory() throws IOException {
		this.directory = Files.createTempDirectory("test");
	}

	@After
	public void destroyDirectory() throws IOException {
		DeletingFileVisitor.deleteRecursivly(this.directory);
	}

	@Test
	public void testMemoryIndex() throws InvalidReference {
		Storage storage = new MemoryStorage(this.separator);
		Indexer indexed = new Indexer(storage);

		Entity e = new Entity("qwe");

		indexed.persist(e);
		Collection<Reference<? extends Entity>> results = indexed.search(Entity.VALUE_EXTRACTOR).find("qwe");

		assertEquals(1, results.size());
		assertEquals(e, Iterables.getOnlyElement(results).materialize());

	}

	@Test
	public void testFileIndexer() throws InvalidReference {
		Storage storage = new FileStorage(this.directory, this.separator);
		Indexer indexed = new Indexer(storage);

		Entity e = new Entity("qwe");

		indexed.persist(e);
		Collection<Reference<? extends Entity>> results = indexed.search(Entity.VALUE_EXTRACTOR).find("qwe");

		assertEquals(1, results.size());
		assertEquals(e, Iterables.getOnlyElement(results).materialize());

	}

	@Test
	public void testFileRelation() throws InvalidReference {
		FileStorage storage = new FileStorage(this.directory, this.separator);
		Indexer indexed = new Indexer(storage);
		storage.setRelationPersister(indexed);

		Entity e1 = new Entity("qwe");
		Entity e2 = new Entity("asd", e1);

		indexed.persist(e2);
		Collection<Reference<? extends Entity>> results = indexed.search(Entity.VALUE_EXTRACTOR).find("qwe");

		assertEquals(1, results.size());
		assertEquals(e1, Iterables.getOnlyElement(results).materialize());

	}
}
